#!python

# mkepub: make an epub document
# (c)2009 Michael Paul Ashton <data@gtf.org>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with this program; if not, write to the Free Software Foundation, Inc.,
#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

"""
Makes an epub from a specification file and a set of source files.

Usage:

mkepub [opts] [-o outfile]

-s, --spec -- spec file
--deps -- print a list of dependencies to stdout
"""

import os, os.path, shutil, sys
#sys.path.append(os.path.join(sys.path[0], 'PyYAML-3.08/lib/yaml'))
#sys.path[0:0] = os.path.join(sys.path[0], 'lxml-2.2.2/build/lib.macosx-10.3-i386-2.5')
import yaml
from urlparse import urlparse

#from xml.etree.cElementTree import Element, ElementTree, SubElement
from lxml.etree import Element, ElementTree, SubElement
from lxml import etree
import lxml.html

import mimetypes, uuid, datetime, zipfile, time

#print "imported"

#sys.exit()

#import html5lib
#from html5lib import treebuilders
#from xml.etree import cElementTree

def isStr(obj):
    return isinstance(obj, str) or isinstance(obj, unicode)

def isSeq(obj):
    return isinstance(obj, tuple) or isinstance(obj, list)

if 0:
    def loadHTML(path):
        """Loads HTML using html5lib and the etree builder.  
        Returns an etree tree (not element)"""
        f = open(path)
        parser = html5lib.HTMLParser(tree=treebuilders.getTreeBuilder("etree", cElementTree))
        return parser.parse(f)
else:
    import subprocess
    def loadHTML(path):
        # process through tidy
        if isStr(path):
            f = open(path, 'r')
        else:
            f = path
        tidy = subprocess.Popen(['../tidy/bin/tidy', '-asxml', '-q', '-wrap', '0'], stdin = f, stdout = subprocess.PIPE)
        #parser = etree.XMLParser(resolve_entities = True, huge_tree = True)
        #tree = etree.parse(f, parser)
        tree = lxml.html.parse(tidy.stdout)
        lxml.html.html_to_xhtml(tree)
        return tree.getroot()

def isXHTML(url):
    fn = urlparse(url).path
    f = open(fn)
    s = f.read(10)
    if s.startswith('<?xml'):
        return True
    return False

def getint(o):
    try:
        return int(o)
    except (ValueError, TypeError):
        return None

def parseTOCAttr(s):
    if s is None:
        return None, None
    i = s.find(':')
    if i < 0:
        l = getint(s)
        if l is None:
            return None, s
        else:
            return l, None
    return getint(s[:i], s[i+1:])

def parseContentsItem(s):
    fields = s.split(':')
    item = {}
    if len(s) > 0:
        if s[0] == '!':
            item['linear'] = 'yes'
    else:
        return {}
    if len(fields) == 0:
        return item
    if len(fields) >= 1:
        item['uri'] = fields[0]
    if len(fields) >= 2:
        item['label'] = fields[1]
    if len(fields) >= 3:
        item['guide'] = fields[2]
    return item

def getFileTitle(root):
    if isStr(root):
        root = loadHTML(root)
    for e in root.getiterator(XHTML + 'h1'):
        return e.text
    for e in root.getiterator(XHTML + 'title'):
        return e.text

def maxDepth(root, depth = 0):
    md = depth
    for e in root:
        d = maxDepth(e, md + 1)
        if d > md:
            md = d
    return md 

def writeNavmapList(tocroot, navroot, navid = 1):
    for e in tocroot:
        navPoint = SubElement(navroot, 'navPoint', id = 'navPoint-%d' % navid, playOrder = str(navid))
        navLabel = SubElement(navPoint, 'navLabel')
        SubElement(navLabel, 'text').text = e.get('label')
        SubElement(navPoint, 'content', src = e.get('uri'))
        navid = navid + 1
        # write next level if any
        if len(e) > 0:
            navid = writeNavmapList(e, navPoint, navid)
    # preserve navid
    return navid

def writeHTML(e, path = sys.stdout):
    if isinstance(path, file):
        f = path
    else:
        f.open(path, 'w')
    #e.nsmap = { None : XHTML_NAMESPACE }
    tree = etree.ElementTree(e)
    #html = tree.getroot().find('html')
    lxml.html.xhtml_to_html(tree)
    f.write("""<?xml version="1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
""")
    f.write(lxml.html.tostring(e, pretty_print = True, include_meta_content_type = False, method = 'xml', encoding = 'ascii'))
    #f.write(etree.tostring(tree))
    if isStr(path):
        f.close()

import re

manifestrule = re.compile(r"[^a-zA-Z0-9-]")

def mkManifestID(s):
    s = os.path.splitext(s)[0]
    return manifestrule.sub('-', s)

# ------------------------

GuideTypes = [
    'cover', 'title-page', 'toc', 'index', 'glossary', 'acknowledgements',
    'bibliography', 'colophon', 'copyright-page', 'dedication',
    'epigraph', 'foreword', 'loi', 'lot', 'notes', 'preface',
    'text', 'other']

# ---------------------------

XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml"
XHTML = "{%s}" % XHTML_NAMESPACE

# ePub XML tag & attribute names, with namespaces

# OPF namespace
OPFURL = 'http://www.idpf.org/2007/opf'
OPFNS = '{' + OPFURL + '}'
# Dublin Core namespace
DCNS = '{http://purl.org/dc/elements/1.1/}'
# Dublin Core Terms namespace
DCTNS = '{http://purl.org/dc/terms/}'
# XSI namespace
XSINS = '{http://www.w3.org/2001/XMLSchema-instance}'

# Dublin Core terms
for t in ('title', 'language', 'identifier',
        'creator', 'contributor', 'publisher',
        'subject', 'description', 'date', 'type', 'format',
        'source', 'relation', 'coverage', 'rights'):
    globals()['DC' + t.upper()] = DCNS + t

# XSI terms
XSITYPE = XSINS + 'type'

# ----------------------------

class Epub:
    def __init__(self, name):
        self.name = name
        # the TOC is stored as an element tree, but not in NCX 
        # format.  it is a tree of 'item' tags with the following 
        # attributes:
        #    uri -- uri of item, if any.
        #    label -- title to be put in TOC
        #    guide -- guide type -- if present, one of the strings in GuideTypes
        # items may have other items as children.
        self.toc = None
        self.srcdir = '.'
        # manifest is stored as an element tree
        self.manifest = Element('manifest')
        # spine is stored as an element tree
        self.spine = Element('spine', toc = 'ncx')
        # guide is stored as an element tree
        self.guide = Element('guide')

    def getFromManifest(self, id):
        for e in self.manifest.getiterator():
            if e.get('id') == id:
                return e
        return None

    def addToManifest(self, src, mimetype = None, index = None):
        fn = os.path.basename(src)
        fid = mkManifestID(fn)
        i = 0
        while not self.getFromManifest(fid) is None:
            i += 1
            fid = fid + str(i)
        if mimetype is None:
            mimetype = mimetypes.guess_type(src)[0]
        if mimetype == 'text/html':
            #if isXHTML(src):
            # doc will be converted to XHTML
                mimetype = 'application/xhtml+xml'
        elif mimetype is None:
            mimetype = 'application/octet-stream'
        e = Element('item', id = fid, href = src)
        if index is None:
            self.manifest.append(e)
        else:
            self.manifest.insert(index, e)
        e.set('media-type', mimetype)
        return fid

    def addTOCItem(self, uri, label, parent):
        self.lastTOCElement = SubElement(parent,
                'item', label = label, uri = uri)

    def addContentsItem(self, item, tocroot):
        """\p item is a dictionary parsed from contents.
        If a toc item is generated, it is added as a child of 
        \p tocroot.  Returns the last toc item generated"""

        # load URI.  If none given, might be a part
        uri = urlparse(item['uri'])
        if uri.scheme != 'file' and uri.scheme != '':
            raise "Only file URIs may be used"
        path = uri.path
        url = path
        if len(uri.fragment) > 0:
            if os.path.isdir(path):
                raise "fragment not allowed for directory"
            url += '#' + uri.fragment

        # contents -- this may contain other items
        contents = item.get('contents', None)

        # if path is a directory, process as such
        if os.path.isdir(path):
            # some attributes not allowed for directories
            for k in item.keys():
                if k in ('guide', 'label', 'mimetype'):
                    raise k + " attribute not allowed for directory"
            # process items in directory
            newroot = tocroot
            if contents is None:
                for p in os.listdir(path):
                    diritem = item
                    diritem['uri'] = os.path.join(path, p)
                    newroot = self.addContentsItem(diritem, tocroot)
                return newroot
            else:
                # process contents on a directory using 
                # existing tocroot
                self.loadContents(contents, tocroot)

        # the item refers to a file.
        # if root is None after this, then this is 
        # a fragment URI and should not be processed as a file reference
        root = None
        scantoc = False
        hlevel = 0
        if len(uri.fragment) == 0:
            root = loadHTML(path)

        # load guide id
        guide = item.get('guide', None)
        if isStr(guide):
            if not guide.lower() in GuideTypes:
                raise "invalid guide type"

        # load label.  read from file if none given.
        label = item.get('label', None)
        if label is None or len(label) <= 0:
            if root is None:
                r = path
            else:
                r = root
            label = getFileTitle(r)

        # linear
        linear = item.get('linear', True)
        if linear == 'no':
            linear = False

        newroot = tocroot

        # add file to manifest
        if not root is None:
            self.addToManifest(path)

        # make TOC item
        newroot = SubElement(tocroot, 'item', uri = url, hlevel = str(hlevel))
        if not guide is None:
            newroot.set('guide', guide)
        if not label is None:
            newroot.set('label', label)
        if not linear:
            newroot.set('linear', 'no')

        # read file into memory as an etree, 
        # preprocess it, and write it into the epub directory, which 
        # must already exist
        if not root is None:
            if guide is None:
                # don't do TOC scanning on guide items

                # do header tag conversion if enabled
                hlevel = getint(item.get('hlevel', self.hlevel))
                if hlevel > 0:
                    self.addTOCAttrsForHTags(root, hlevel)

                # scan tags into TOC
                scantoc = item.get('scantoc', self.scantoc)
                if scantoc:
                    self.scanTOCTags(root, path, newroot)
                
            # write to epub
            dest = self.mkDest(path)
            f = open(dest, 'w')
            #f.write("""<?xml version="1.0" encoding="UTF-8"?>
#      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
#""")
            #root.set('xmlns', "http://www.w3.org/1999/xhtml")
            writeHTML(root, f)
            #tree = etree.ElementTree(root)
            #tree.write(f)
            f.close()

        # if we have a contents attrib, process it
        if not contents is None:
            self.loadContents(contents, newroot)

        return newroot

    def loadContents(self, contents, tocroot = None):
        """Load the `contents` structure given in a spec file"""
        if tocroot is None:
            tocroot = self.toc
        for item in contents:
            if isStr(item):
                i = parseContentsItem(item)
                self.addContentsItem(i, tocroot)
            elif isinstance(item, dict):
                self.addContentsItem(item, tocroot)

    def loadOther(self, other):
        if isStr(other):
            other = [ other ]
        for item in other:
            if os.path.isdir(item):
                self.loadOther(os.listdir(item))
            elif not os.path.exists(item):
                raise "item in other section doesn't exist"

    def mkSpine(self):
        """Make the spine from the manifest.

        This adds IDs for all of the files with type
        application/xhtml+xml."""
        self.spine = Element('spine', toc = 'ncx')
        for e in self.manifest.getiterator():
            if e.get('media-type') == 'application/xhtml+xml':
                SubElement(self.spine, 'itemref', idref = e.get('id'))

    def mkGuide(self):
        """Make the guide from the TOC.

        This adds entries for all of the TOC elements which have 
        a legal guide attribute."""
        self.guide = Element('guide')
        for e in self.toc.getiterator('item'):
            uri = e.get('uri', None)
            if uri is None: continue
            guideType = e.get('guide', None)
            if guideType is None: continue
            if not guideType in GuideTypes:
                raise "unknown guide type found in TOC"
            label = e.get('label')
            SubElement(self.guide, 'reference', type = guideType, title = label, href = uri)

    def addMetadataElement(self, infod, tag, infofield, 
            default = None, multiple = False,
            attrs = None, default_attr = None):
        t = infod.get(infofield, default)
        e = None
        if not t is None:
            if isSeq(t):
                if not multiple:
                    raise "multiple values not allowed for info tag " + infofield
            else:
                t = ( t, )
            for v in t:
                e = SubElement(self.info, tag)
                print v
                if isinstance(v, dict):
                    if attrs is None:
                        raise "attributes not allowed for info field " + infofield
                    if default_attr is None:
                        raise "!!! need a default_attr arg here"
                    defattr = v.get(default_attr, None)
                    if defattr is None:
                        raise "attribute %s required on info field %s" %(defattr, infofield)
                    e.text = defattr
                    for fieldattr, tagattr in attrs.iteritems():
                        av = v.get(fieldattr, None)
                        if not av is None:
                            e.set(tagattr, av)
                else:
                    e.text = v
        return e

    def loadInfo(self, info):
        self.info = Element('metadata')

        # --- title
        title = info.get('title', None)
        if title is None:
            # find the title-page label
            # (note that this assumes the guide has been made)
            for e in self.guide.getiterator():
                if e.get('type') == 'title-page':
                    title = e.get('title', None)
                    break
        if title is None:
            raise "no title"
        e = SubElement(self.info, DCTITLE)
        e.text = title
        self.title = title

        # --- language
        #!!!TODO check for validity
        self.addMetadataElement(info, DCLANGUAGE, 'language', 'en')

        # --- UUID
        self.identifier = info.get('identifier', str(uuid.uuid4()))
        e = SubElement(self.info, DCIDENTIFIER)
        e.text = self.identifier

        # --- Various optional elements which need no special processing
        self.addMetadataElement(info, DCSUBJECT, 'subject', multiple = True)
        self.addMetadataElement(info, DCDESCRIPTION, 'description')
        self.addMetadataElement(info, DCRELATION, 'relation', multiple = True)
        self.addMetadataElement(info, DCPUBLISHER, 'publisher', multiple = True)
        self.addMetadataElement(info, DCRIGHTS, 'rights', multiple = True)
        self.addMetadataElement(info, DCTYPE, 'type')
        self.addMetadataElement(info, DCFORMAT, 'format')
        self.addMetadataElement(info, DCSOURCE, 'source', multiple = True)

        # --- creation date
        create_date = info.get('creation_date', datetime.date.today().isoformat())
        e = SubElement(self.info, DCDATE, event = 'creation')
        e.text = create_date

        # --- creator
        self.addMetadataElement(info, DCCREATOR, 'creator',
                multiple = True,
                attrs = { 'role' : 'role', 'file_as' : 'file-as' },
                default_attr = 'name')

        # --- contributor
        self.addMetadataElement(info, DCCONTRIBUTOR, 'contributor',
                multiple = True,
                attrs = { 'role' : 'role' },
                default_attr = 'name')

    def loadOptions(self, opts):
        self.scantoc = opts.get('scantoc', True)
        if isStr(self.scantoc):
            if self.scantoc.lower() in ('true', 'yes'):
                self.scantoc = True
        elif not self.scantoc:
            self.scantoc = False

        self.hlevel = opts.get('hlevel', 2)
        if isStr(self.hlevel):
            self.hlevel = getInt(self.hlevel)
        if self.hlevel < 1:
            self.hlevel = 0

        self.name = opts.get('name', None)

    def loadFromSpec(self, path, epubname = None):
        self.specpath = path
        f = open(path, 'r')
        self.spec = yaml.load(f)
        f.close()

        # read options
        self.loadOptions(self.spec.get('options', {}))
        print 'scantoc', self.scantoc

        # make epub directory
        if epubname is None:
            epubname = self.name
        else:
            self.name = epubname
        if os.path.exists(epubname):
            raise "epub file or directory already exists"
        os.makedirs(epubname)

        #self.initTOC()
        self.toc = Element("toc")
        self.lastID = 0

        # read contents
        if self.spec.has_key('contents'):
            self.loadContents(self.spec['contents'])
        else:
            raise "no contents section"

        # read other files
        if self.spec.has_key('other'):
            self.loadOther(self.spec['other'])

        print etree.tostring(self.toc)

        # make spine & guide
        self.mkSpine()
        self.mkGuide()

        # read info
        inf = self.spec.get('info', None)
        if not inf is None:
            self.loadInfo(inf)
        else:
            raise "no info section"

    def mkdir(self, path = '.'):
        self.outdir = os.path.join(path, self.name)
        if not os.path.exists(self.outdir):
            os.mkdir(self.outdir)

    if 0:
        def addTOCItem(self, uri, label, level):
            #print uri, repr(label), level
            if level < 1:
                return

            # adjust level if needed
            if level > self.currentTOCLevel():
                if (level - self.currentTOCLevel()) > 1:
                    return
                self.tocstack.append(self.lastTOCElement)
            elif level < self.currentTOCLevel():
                self.tocstack = self.tocstack[0:level]

            # add element
            self.lastTOCElement = SubElement(self.tocstack[-1], 'item', label = label, uri = uri)

    def addTOCAttrsForHTags(self, root, hlevel):
        """For each tag in the element tree rooted at \p root, adds toclevel 
        and toclabel attributes to header tags.  \p hlevel determines the 
        highest level of header tag which will be treated.  Typically this 
        value will be 2, as h1 tags are normally used to display the 
        document title.  The highest TOC level is 1, so toclevel attributes 
        begin at level 2.

        Neither attribute is added if an attribute exists on the header tag 
        already.
        """
        if hlevel < 2:
            return
        for e in root.getiterator():
            if isStr(e.tag) and e.tag[len(XHTML)] == 'h':
                if e.get('toclevel') is None:
                    hl = getint(e.tag[len(XHTML) + 1])
                    if not hl is None:
                        if hl >= hlevel:
                            tl = hl - hlevel + 2
                            e.set('toclevel', str(tl))

    def scanTOCTags(self, root, baseuri, tocroot):
        """For each element in \p root which has a toclevel 
        attribute, an item is added to tocroot.  The item points to 
        a URI composed of \p baseuri and the value of the id 
        attribute on the element; if none is present, one is generated.
        """
        #print baseuri

        tocstack = [ tocroot ]
        lastTOCElement = tocroot

        for e in root.getiterator():
            toclevel = getint(e.get('toclevel', None))
            if toclevel is None:
                continue

            # label
            label = e.get('toclabel', None)
            if label is None:
                label = e.text

            # ID attribute
            id = e.get('name', None)
            if id is None:
                id = e.get('id', None)
            else:
                del e.attrib['name']
            if id is None:
                self.lastID += 1
                id = 'toc' + str(self.lastID)
            e.set('id', id)

            uri = baseuri + '#' + id
            if label is None:
                label = e.text
                if label is None:
                    raise "No TOC label"

            # adjust level
            if toclevel > len(tocstack):
                if (toclevel - len(tocstack)) == 1:
                    tocstack.append(lastTOCElement)
            elif toclevel < len(tocstack):
                tocstack = tocstack[0:toclevel]

            # add element
            lastTOCElement = self.addTOCItem(uri, label, tocstack[-1])
            #lastTOCElement = SubElement(self.tocstack[-1], 'item', label = label, uri = uri)

    def srcPath(self, src):
        srcdir = self.spec.get('srcdir', '.')
        srcpath = os.path.normpath(os.path.join(srcdir, src))
        return srcpath
 
    def destPath(self, src):
        return os.path.normpath(os.path.join(self.name, src))

    def mkDest(self, src):
        """Given source path \p src, 
        returns a complete path to the equivalent destination file, 
        and a complete path to the source file, as tuple 
        (srcpath, destpath); also creates any necessary directories, and 
        adds the destination path to the manifest list, and assigns it 
        a unique ID."""
        srcpath = self.srcPath(src)
        destpath = self.destPath(src)
        destdir = destpath
        if not os.path.isdir(destdir):
            destdir = os.path.dirname(destdir)
            if not os.path.isdir(destdir):
                os.makedirs(destdir)
        return destpath
        # add to manifest
        #return self.addToManifest(src)

    def writeNCX(self, path):
        root = Element('ncx', version='2005-1', xmlns = "http://www.daisy.org/z3986/2005/ncx/")

        tocdepth = maxDepth(self.toc)

        # head
        head = SubElement(root, 'head')
        SubElement(head, 'meta', name = 'dtb:uid', content = self.identifier)
        SubElement(head, 'meta', name = 'dtb:depth', content = str(tocdepth))
        SubElement(head, 'meta', name = 'dtb:totalPageCount', content = '0')
        SubElement(head, 'meta', name = 'dtb:maxPageNumber', content = '0')

        # title
        title = SubElement(root, 'docTitle')
        SubElement(title, 'text').text = self.title

        # navmap
        navmap = SubElement(root, 'navMap')
        writeNavmapList(self.toc, navmap)

        # write file
        dest = self.destPath(path)
        tree = ElementTree(root)
        f = open(dest, 'w')
        f.write("""<?xml version="1.0"?>
      <!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN" "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">
""")
        tree.write(f)
        f.close()

        return path

    def writeOPF(self, path):
        dest = self.destPath(path)

        f = open(dest, 'w')
        f.write("""<?xml version="1.0"?>

      <package xmlns="http://www.idpf.org/2007/opf" unique-identifier="dcidid" version="2.0">

""")

        tree = ElementTree(self.info)
        tree.write(f)
        f.write("\n\n")

        tree = ElementTree(self.manifest)
        tree.write(f)
        f.write("\n\n")

        tree = ElementTree(self.spine)
        tree.write(f)
        f.write("\n\n")

        tree = ElementTree(self.guide)
        tree.write(f)

        f.write("""

</package>
""")
        f.close()

        return path

    def write(self):
        # make directory
        if not os.path.exists(self.name):
            os.makedirs(self.name)
 
        # generate toc.ncx, add to manifest
        toc = self.writeNCX('toc.ncx')
        tocid = self.addToManifest(toc, 'application/x-dtbncx+xml', 0)
        self.spine.set('toc', tocid)
 
        # generate content.opf
        opf = self.writeOPF('content.opf')
 
        # write META-INF & container.xml
        metainf = os.path.join(self.name, 'META-INF')
        if not os.path.exists(metainf):
            os.makedirs(metainf)
        ctr = os.path.join(metainf, 'container.xml')
        ctrf = open(ctr, 'w')
        ctrf.write("""<?xml version="1.0"?>
      <container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
         <rootfiles>
            <rootfile full-path="%s" media-type="application/oebps-package+xml"/>
         </rootfiles>
      </container>
"""%opf)
        ctrf.close()

        # copy files in manifest
        etree.dump(self.manifest)
        for e in self.manifest.getiterator('item'):
            dest = self.destPath(e.get('href'))
            src = self.srcPath(e.get('href'))
            if not os.path.exists(dest):
                destdir = os.path.basename(dest)
                if not os.path.exists(destdir):
                    os.makedirs(destdir)
                shutil.copy(src, dest)

        # make zip file
        cwd = os.getcwd()
        try:
            z = zipfile.ZipFile(self.name + '.epub', 'w')
            #zi = zipfile.ZipInfo()
            #zi.filename = 'mimetype'
            #zi.date_time = time.localtime()[0:6]
            #zi.compress_type = zipfile.ZIP_STORED
            z.writestr('mimetype', 'application/epub+zip')
            os.chdir(self.name)
            for root, dirs, files in os.walk('.'):
                for fn in files:
                    z.write(os.path.join(root, fn), os.path.join(root, fn), zipfile.ZIP_DEFLATED)
            z.close()
        finally:
            os.chdir(cwd)

from optparse import OptionParser

def main():
    parser = OptionParser()
    parser.add_option('-s', '--spec',
            help = 'spec file', default = None)
    parser.add_option('--deps', action='store_true',
            help = 'print dependencies to stdout', default = False)
    parser.add_option('-o', dest='outfile',
            help = 'name of output epub, without extension', default = None)

    opts, args = parser.parse_args()

    if opts.spec is None:
        parser.error('no spec file')

    # make epub object from spec file
    epub = Epub(opts.outfile)
    epub.loadFromSpec(opts.spec, opts.outfile)
    print epub.spec
    
    epub.write()

    print "MANIFEST"
    print
    etree.dump(epub.manifest)
    print
    print "TOC"
    print
    etree.dump(epub.toc)
    print
    print "SPINE"
    print
    etree.dump(epub.spine)
    print
    print "GUIDE"
    print
    etree.dump(epub.guide)
    print
    print "INFO"
    print
    etree.dump(epub.info)
    print
     # write toc.ncx
    # write content.opf
    # write mimetype

main()

